<?php
/*********************************************************************************
 * Speek is a conference call management system developed by
 * Communiclique, Inc.
 * 
 * Copyright (C) 2010 Communiclique Inc.
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License version 3 as published by the
 * Free Software Foundation with the addition of the following permission added
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 * 
 * You can contact Communiclique, Inc. headquarters at Teqcorner,LLC, McLean, VA
 * General informational inquiries: info@speek.com
 * 
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU General Public License version 3.
 * 
 * In accordance with Section 7(b) of the GNU General Public License version 3,
 * these Appropriate Legal Notices must retain the display of the "Powered by
 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
 * technical reasons, the Appropriate Legal Notices must display the words
 * "Powered by SugarCRM".
 ********************************************************************************/

//
/**
 * schedule_speeek class -- bridge class between the sugar CRM and
 * speek call server. Used to schedule calls initiated by the sugar
 * CRM UI.
 * 
 * Details regarding the speek api may be found at:
 *           http://developer.speek.com/
 * 
 * 
 * This class represents the simplest (brute force) approach to using the
 * speek conference API to execute conference calls scheduled with the Sugar UI.
 * 
 * In a nutshell, any time a call is saved (from sugar), and if the speek
 * checkbox is set, old speek calls are deleted and a new one scheduled.
 * 
 * Future extensions of this class will use Sugar to compare schedule times
 * and participant lists to detect a change in call state - which would 
 * then allow clients to simply set participant lists of an existing call
 * rather than a delete/add sequence.
 * 
 * The simplest brute force approach is:
 * 
 *   - if the checkbox (from the bean) is set we are going to ALWAYS create a new 
 *       speek call
 *   - if a previous call ID is set, that call will be deleted before
 *       the new call is created
 *            
 * 
 */
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

	class speek{

		const NO_CALL_NUMBERS = 0;
		const SPEEK_URL = 'http://api.speek.com/calls/';
		const SPEEK_KEY = 'Your_license_key_here';
		const CALL_NOW_TOKEN = 'NOW';
		
		/**
		 * 'Pre-save' allows us to see of the request box is checked (from the bean) to
		 * see if a call needs to be deleted
		 * 
		 * Note that the current restriction of the speek call API limits the
		 * client to no more than one call per second - hence the 1.1 second delay
		 * between deleting previous calls and creating new calls.
		 * 
		 * @param unknown_type $bean
		 * @param unknown_type $event
		 * @param unknown_type $arguments
		 * @return $bean->speek_call_id_c set to speek call ID
		 */
		public function schedule(&$bean,$event,$arguments) {
			global $current_user;
			
			// Any previous call is deleted (this can be made smarter later
			// with a more detailed API - or some type of 'memory' that allows
			// us to compare existing call numbers with new call numbers, etc...
			// But for now - always throw out the previuous call and create a new one
			//
			$callID = $bean->fetched_row['speek_call_id_c'];
			
			if ($callID) { // informs us of a request to 'unspeek' an existing call			   
			    $this->deleteCall($callID);			
				$bean->speek_call_id_c = ''; // clears the display field				
			 	}			
			
			if ($bean->use_speek_c == 1) { // use speek checkbox is ON  - create a new call
				if ($callID) usleep(1100000);
                $newCallID = $this->addCall($bean);	
                $bean->speek_call_id_c = $newCallID; // resets the display field
				
			} // use speek checkbox is ON		
		}
	
		
		/**
		 * addCall - pull all the data from sugar database to make API call
		 * to schedule a call
		 * 
		 * @param unknown_type $bean
		 * @return unknown_type
		 */
		public function addCall(&$bean) { // add a new call
			global $current_user;
			
			// First thing - get an array of the phone numbers to call
			//
			$numbers = $this->getCallNumbers($bean);
			if (count($numbers) == 0) return self::NO_CALL_NUMBERS;
			//
			// Get the number from the call initiator
			$ownerNumber = $this->getOwnerNumber($bean);
			if (count($ownerNumber) != 0) { // we may want log this if no numbers - but not now
				$numbers[] = $ownerNumber[0];
			} // we may want to log this
			
			
			if ($bean->speek_call_now_c) { // checkbox to issue call immediately
				$speekDate = self::CALL_NOW_TOKEN;
			}	

			else {
				$sugarDate = $bean->date_start;
				$tz = $current_user->getPreference('timezone');
			
				$uDate = strtotime($sugarDate." ".$tz);
				$callTime = gmstrftime("%Y-%m-%d %H:%M",$uDate);
				$sugarTODArray = explode(" ",$callTime);
				$speekDate = $sugarTODArray[0];
				$speekTime = $sugarTODArray[1];
				
			}
			
			$callNumbers = "";
			foreach ($numbers as $n) { 
				$nN = str_replace('(','',$n);
				$nN = str_replace(')','',$nN);
				$nN = str_replace('-','',$nN);
				$nN = str_replace(' ','',$nN);
				$nN = str_replace('.','',$nN);
				if (strlen($nN)==10) $nN = '1'.$nN; // adding a presumed missing '1' prefix
				$callNumbers .=  $nN . ",";
			}
			
			$callNumbers = substr($callNumbers,0,strlen($callNumbers)-1);
			$callID = $this->scheduleSpeekCall($callNumbers,$speekDate,$speekTime);
			return $callID;
			
		} // add a new call		

		/**
		 * getCallNumbers - queries the bean array for contacts_arr to extract
		 * the UIDs for the call contacts
		 * numbers
		 * @param unknown_type $bean
		 * @return rowset result 
		 */
		public function getCallNumbers(&$bean) {
			$rV = array();
				// get the contact IDs
			$db = DBManagerFactory::getInstance();					
			if (count($bean->contacts_arr) != 0) {
				
			foreach ($bean->contacts_arr as $nC) {
		   
		    	$query2 = "SELECT contacts.phone_work,contacts.phone_mobile
			         FROM contacts
			         WHERE contacts.`id` = '".$nC."'";
			
				$result2 = $db->query($query2,true);
		    	$numbers = $db->fetchByAssoc($result2); // should be only one row
		    	// pass the first non-blank entry into the numbers array
		    	if ($numbers['phone_work']) {
		    		$rV[] = $numbers['phone_work'];
		    	}
		    	else $rV[] = $numbers['phone_mobile'];
		    	
		      }
			}
		    
		    //
		    // Now check the user numbers
		    //
		    if (count($bean->users_arr) == 0) return $rV;
		    
			foreach ($bean->users_arr as $nC) {
		   
		    	$query2 = "SELECT users.phone_work,users.phone_mobile
			         FROM users
			         WHERE users.`id` = '".$nC."'";
			
				$result2 = $db->query($query2,true);
		    	$numbers = $db->fetchByAssoc($result2); // should be only one row
		    	// pass the first non-blank entry into the numbers array
		    	if ($numbers['phone_work']) {
		    		$rV[] = $numbers['phone_work'];
		    	}
		    	else $rV[] = $numbers['phone_mobile'];
		    	
		    }    
			
		return $rV;
			
		}
		
		/**
		 * getOwner number - returns the phone number of the person 
		 * initiating the call. This should have been just a specialized
		 * version of getCallNumbers - but different keys and tables are used -
		 * simplest and fastest was is simple cut and paste
		 * 
		 * @param unknown_type $bean
		 * @return array holding only 1 number for the call initiator. (Keeping the
		 * return value an array so refactoring may use common code in the getCallNumbers method)
		 * 
		 */
		public function getOwnerNumber(&$bean) { 
			$rV = array();
			$userId = $bean->assigned_user_id;   // call ID in 'sugar' database
			
			// get the contact IDs
			$db = DBManagerFactory::getInstance();
				    	
		    $query2 = "SELECT users.phone_work,users.phone_mobile
			         FROM users
			         WHERE users.`id` = '".$userId."'";
			
			$result2 = $db->query($query2,true);
		    $numbers = $db->fetchByAssoc($result2); // should be only one row
		    // pass the first non-blank entry into the numbers array
		    if ($numbers['phone_work']) {
		    		$rV[] = $numbers['phone_work'];
		    }
		    else $rV[] = $numbers['phone_mobile'];
		    
		 return $rV;		
			
		}
		
		public function scheduleSpeekCall($numbers,$date,$time) {
			try {	    
		     if ($date != self::CALL_NOW_TOKEN) {
		     	$callRequest = self::SPEEK_URL.'scheduleCall?api_key='.self::SPEEK_KEY;
		     	$callRequest .= "&date=".urlencode($date);
		     	$callRequest .= "&time=".urlencode($time);
		     }
		     else $callRequest = self::SPEEK_URL.'callNow?api_key='.self::SPEEK_KEY;
		     
		     $callRequest .= "&numbers=".urlencode($numbers);
		     $callRequest .= "&format=json";
		     $ch = curl_init($callRequest);
		     curl_setopt($ch,CURLOPT_POST,1);
		     curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
		     $rV = curl_exec($ch);
		     curl_close($ch);
		     $rV2 = json_decode($rV,true);
		     $cId = 0;
		     $ok = $rV2['status']['ok'];
		     if ($ok) {
		     	$cId =  ($rV2['results']['call_id']);
		     }
			 return $cId;
			}
			catch(Exception $e){
			  return 0;
			}			
		}
		
		/** calls are deleted if the user de-selected the 'use speek' checkbox,
		 * or when reschedule a call
		 * 
		 * @param $callID
		 * @return unknown_type
		 */
		public function deleteCall($callID) {
		try {
		     $callRequest = self::SPEEK_URL.'deleteCall?api_key='.self::SPEEK_KEY;
		     $callRequest .= "&call_id=$callID";
		     $callRequest .= "&format=json";
		     $ch = curl_init($callRequest);
		     curl_setopt($ch,CURLOPT_POST,1);
		     curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
		     $rV = curl_exec($ch);
		     curl_close($ch);
		     $rV = json_decode($rV,true);
		   
		     return (1 == $rV['status']['ok']);
			}
			catch(Exception $e){
			  return false;
			}				
		}
		
		/**
		*  TODO
		 * check certain fields to see if any of the details of the call
		 * has changed:
		 *   Sugar:subject <-> speek:call name
		 *   Sugar:descriptin <-> speek:description
		 *   Sugar:time <-> speek:time
		 *   
		 * @param unknown_type $bean
		 * @return unknown_type
		 */
		public function callChanged(&$bean) {
			return false;    // return false for now
		}
	}

?>